using System;
using System.Collections;
using System.IO;
using System.Collections.Generic;


namespace System.Windows.Media.DirectShow.Vorbis
{

    public abstract class Floor
    {

        public static float[] DB_STATIC_TABLE ={
		   1.0649863e-07f, 1.1341951e-07f, 1.2079015e-07f, 1.2863978e-07f,
		   1.3699951e-07f, 1.4590251e-07f, 1.5538408e-07f, 1.6548181e-07f,
		   1.7623575e-07f, 1.8768855e-07f, 1.9988561e-07f, 2.128753e-07f,
		   2.2670913e-07f, 2.4144197e-07f, 2.5713223e-07f, 2.7384213e-07f,
		   2.9163793e-07f, 3.1059021e-07f, 3.3077411e-07f, 3.5226968e-07f,
		   3.7516214e-07f, 3.9954229e-07f, 4.2550680e-07f, 4.5315863e-07f,
		   4.8260743e-07f, 5.1396998e-07f, 5.4737065e-07f, 5.8294187e-07f,
		   6.2082472e-07f, 6.6116941e-07f, 7.0413592e-07f, 7.4989464e-07f,
		   7.9862701e-07f, 8.5052630e-07f, 9.0579828e-07f, 9.6466216e-07f,
		   1.0273513e-06f, 1.0941144e-06f, 1.1652161e-06f, 1.2409384e-06f,
		   1.3215816e-06f, 1.4074654e-06f, 1.4989305e-06f, 1.5963394e-06f,
		   1.7000785e-06f, 1.8105592e-06f, 1.9282195e-06f, 2.0535261e-06f,
		   2.1869758e-06f, 2.3290978e-06f, 2.4804557e-06f, 2.6416497e-06f,
		   2.8133190e-06f, 2.9961443e-06f, 3.1908506e-06f, 3.3982101e-06f,
		   3.6190449e-06f, 3.8542308e-06f, 4.1047004e-06f, 4.3714470e-06f,
		   4.6555282e-06f, 4.9580707e-06f, 5.2802740e-06f, 5.6234160e-06f,
		   5.9888572e-06f, 6.3780469e-06f, 6.7925283e-06f, 7.2339451e-06f,
		   7.7040476e-06f, 8.2047000e-06f, 8.7378876e-06f, 9.3057248e-06f,
		   9.9104632e-06f, 1.0554501e-05f, 1.1240392e-05f, 1.1970856e-05f,
		   1.2748789e-05f, 1.3577278e-05f, 1.4459606e-05f, 1.5399272e-05f,
		   1.6400004e-05f, 1.7465768e-05f, 1.8600792e-05f, 1.9809576e-05f,
		   2.1096914e-05f, 2.2467911e-05f, 2.3928002e-05f, 2.5482978e-05f,
		   2.7139006e-05f, 2.8902651e-05f, 3.0780908e-05f, 3.2781225e-05f,
		   3.4911534e-05f, 3.7180282e-05f, 3.9596466e-05f, 4.2169667e-05f,
		   4.4910090e-05f, 4.7828601e-05f, 5.0936773e-05f, 5.4246931e-05f,
		   5.7772202e-05f, 6.1526565e-05f, 6.5524908e-05f, 6.9783085e-05f,
		   7.4317983e-05f, 7.9147585e-05f, 8.4291040e-05f, 8.9768747e-05f,
		   9.5602426e-05f, 0.00010181521f, 0.00010843174f, 0.00011547824f,
		   0.00012298267f, 0.00013097477f, 0.00013948625f, 0.00014855085f,
		   0.00015820453f, 0.00016848555f, 0.00017943469f, 0.00019109536f,
		   0.00020351382f, 0.00021673929f, 0.00023082423f, 0.00024582449f,
		   0.00026179955f, 0.00027881276f, 0.00029693158f, 0.00031622787f,
		   0.00033677814f, 0.00035866388f, 0.00038197188f, 0.00040679456f,
		   0.00043323036f, 0.00046138411f, 0.00049136745f, 0.00052329927f,
		   0.00055730621f, 0.00059352311f, 0.00063209358f, 0.00067317058f,
		   0.00071691700f, 0.00076350630f, 0.00081312324f, 0.00086596457f,
		   0.00092223983f, 0.00098217216f, 0.0010459992f, 0.0011139742f,
		   0.0011863665f, 0.0012634633f, 0.0013455702f, 0.0014330129f,
		   0.0015261382f, 0.0016253153f, 0.0017309374f, 0.0018434235f,
		   0.0019632195f, 0.0020908006f, 0.0022266726f, 0.0023713743f,
		   0.0025254795f, 0.0026895994f, 0.0028643847f, 0.0030505286f,
		   0.0032487691f, 0.0034598925f, 0.0036847358f, 0.0039241906f,
		   0.0041792066f, 0.0044507950f, 0.0047400328f, 0.0050480668f,
		   0.0053761186f, 0.0057254891f, 0.0060975636f, 0.0064938176f,
		   0.0069158225f, 0.0073652516f, 0.0078438871f, 0.0083536271f,
		   0.0088964928f, 0.009474637f, 0.010090352f, 0.010746080f,
		   0.011444421f, 0.012188144f, 0.012980198f, 0.013823725f,
		   0.014722068f, 0.015678791f, 0.016697687f, 0.017782797f,
		   0.018938423f, 0.020169149f, 0.021479854f, 0.022875735f,
		   0.024362330f, 0.025945531f, 0.027631618f, 0.029427276f,
		   0.031339626f, 0.033376252f, 0.035545228f, 0.037855157f,
		   0.040315199f, 0.042935108f, 0.045725273f, 0.048696758f,
		   0.051861348f, 0.055231591f, 0.058820850f, 0.062643361f,
		   0.066714279f, 0.071049749f, 0.075666962f, 0.080584227f,
		   0.085821044f, 0.091398179f, 0.097337747f, 0.10366330f,
		   0.11039993f, 0.11757434f, 0.12521498f, 0.13335215f,
		   0.14201813f, 0.15124727f, 0.16107617f, 0.17154380f,
		   0.18269168f, 0.19456402f, 0.20720788f, 0.22067342f,
		   0.23501402f, 0.25028656f, 0.26655159f, 0.28387361f,
		   0.30232132f, 0.32196786f, 0.34289114f, 0.36517414f,
		   0.38890521f, 0.41417847f, 0.44109412f, 0.46975890f,
		   0.50028648f, 0.53279791f, 0.56742212f, 0.60429640f,
		   0.64356699f, 0.68538959f, 0.72993007f, 0.77736504f,
		   0.82788260f, 0.88168307f, 0.9389798f, 1.0f};

        public static Floor CreateInstance(IBitStream source, SetupHeader header)
        {

            int type = source.ReadInt32(16);
            switch (type)
            {
                case 0:
                    //System.out.println("floor type 0");
                    throw new VorbisFormatException("Floor type " + type + " is not supported.");
                case 1:
                    //System.out.println("floor type 1");
                    return new Floor1(source, header);
                default:
                    throw new VorbisFormatException("Floor type " + type + " is not supported.");
            }
        }

        public abstract int Type { get; }
        public abstract Floor DecodeFloor(VorbisStream vorbis, IBitStream source);
        public abstract void ComputeFloor(float[] vector);

    }

    public class Floor0 : Floor
    {

        private int order, rate, barkMapSize, amplitudeBits, amplitudeOffset;
        private int[] bookList;

        public Floor0(IBitStream source, SetupHeader header)
        {

            order = source.ReadInt32(8);
            rate = source.ReadInt32(16);
            barkMapSize = source.ReadInt32(16);
            amplitudeBits = source.ReadInt32(6);
            amplitudeOffset = source.ReadInt32(8);

            int bookCount = source.ReadInt32(4) + 1;
            bookList = new int[bookCount];

            for (int i = 0; i < bookList.Length; i++)
            {
                bookList[i] = source.ReadInt32(8);
                if (bookList[i] > header.CodeBooks.Length)
                {
                    throw new VorbisFormatException("A floor0_book_list entry is higher than the code book count.");
                }
            }
        }

        public override int Type
        {
            get
            {
                return 0;
            }
        }

        public override Floor DecodeFloor(VorbisStream vorbis, IBitStream source)
        {
            /** @todo implement */
            throw new NotImplementedException();
        }

        public override void ComputeFloor(float[] vector)
        {
            /** @todo implement */
            throw new NotImplementedException();
        }
    }


    public class Floor1 : Floor, ICloneable
    {

        private int[] partitionClassList;
        private int maximumClass, multiplier, rangeBits;
        private int[] classDimensions;
        private int[] classSubclasses;
        private int[] classMasterbooks;
        private int[][] subclassBooks;
        private int[] xList;
        private int[] yList;
        private int[] lowNeighbours, highNeighbours;

        private int[] RANGES = new int[] { 256, 128, 86, 64 };

        private Floor1()
        {
        }

        public Floor1(IBitStream source, SetupHeader header)
        {

            maximumClass = -1;
            int partitions = source.ReadInt32(5);
            partitionClassList = new int[partitions];

            for (int i = 0; i < partitionClassList.Length; i++)
            {
                partitionClassList[i] = source.ReadInt32(4);
                if (partitionClassList[i] > maximumClass)
                {
                    maximumClass = partitionClassList[i];
                }
            }

            classDimensions = new int[maximumClass + 1];
            classSubclasses = new int[maximumClass + 1];
            classMasterbooks = new int[maximumClass + 1];
            subclassBooks = new int[maximumClass + 1][];

            int xListLength = 2;

            for (int i = 0; i <= maximumClass; i++)
            {
                classDimensions[i] = source.ReadInt32(3) + 1;
                xListLength += classDimensions[i];
                classSubclasses[i] = source.ReadInt32(2);

                if (classDimensions[i] > header.CodeBooks.Length ||
                   classSubclasses[i] > header.CodeBooks.Length)
                {
                    throw new VorbisFormatException("There is a class dimension or class subclasses entry higher than the number of codebooks in the setup header.");
                }
                if (classSubclasses[i] != 0)
                {
                    classMasterbooks[i] = source.ReadInt32(8);
                }
                subclassBooks[i] = new int[1 << classSubclasses[i]];
                for (int j = 0; j < subclassBooks[i].Length; j++)
                {
                    subclassBooks[i][j] = source.ReadInt32(8) - 1;
                }
            }

            multiplier = source.ReadInt32(2) + 1;
            rangeBits = source.ReadInt32(4);

            List<Int32> alXList = new List<Int32>();

            alXList.Add((int)(0));
            alXList.Add((int)(1 << rangeBits));

            for (int i = 0; i < partitions; i++)
            {
                for (int j = 0; j < classDimensions[partitionClassList[i]]; j++)
                {
                    alXList.Add((int)(source.ReadInt32((uint)rangeBits)));
                }
            }

            xList = new int[alXList.Count];
            for (int i = 0; i < alXList.Count; i++)
            {
                xList[i] = (int)alXList[i];
            }

            lowNeighbours = new int[xList.Length];
            highNeighbours = new int[xList.Length];

            for (int i = 0; i < xList.Length; i++)
            {
                lowNeighbours[i] = Util.LowNeighbour(xList, i);
                highNeighbours[i] = Util.HighNeighbour(xList, i);
            }
        }

        public override int Type
        {
            get
            {
                return 1;
            }
        }

        public override Floor DecodeFloor(VorbisStream vorbis, IBitStream source)
        {

            if (!source.ReadBit())
            {
                return null;
            }

            Floor1 clone = (Floor1)Clone();

            clone.yList = new int[xList.Length];

            int range = RANGES[multiplier - 1];

            clone.yList[0] = source.ReadInt32(Util.Ilog(range - 1));
            clone.yList[1] = source.ReadInt32(Util.Ilog(range - 1));

            int offset = 2;

            for (int i = 0; i < partitionClassList.Length; i++)
            {
                int cls = partitionClassList[i];
                int cdim = classDimensions[cls];
                int cbits = classSubclasses[cls];
                int csub = (1 << cbits) - 1;
                int cval = 0;
                if (cbits > 0)
                {
                    cval = vorbis.SetupHeader.CodeBooks[classMasterbooks[cls]].ReadInt32(source);
                }

                for (int j = 0; j < cdim; j++)
                {
                    int book = subclassBooks[cls][cval & csub];
                    cval >>= cbits;
                    if (book >= 0)
                    {
                        clone.yList[j + offset] = vorbis.SetupHeader.CodeBooks[book].ReadInt32(source);
                    }
                    else
                    {
                        clone.yList[j + offset] = 0;
                    }
                }
                offset += cdim;
            }

            return clone;
        }

        public override void ComputeFloor(float[] vector)
        {

            int n = vector.Length;
            int values = xList.Length;
            bool[] step2Flags = new bool[values];

            int range = RANGES[multiplier - 1];

            for (int i = 2; i < values; i++)
            {
                int lowNeighbourOffset = lowNeighbours[i];//Util.lowNeighbour(xList, i);
                int highNeighbourOffset = highNeighbours[i];//Util.highNeighbour(xList, i);
                int predicted = Util.RenderPoint(
                   xList[lowNeighbourOffset], xList[highNeighbourOffset],
                   yList[lowNeighbourOffset], yList[highNeighbourOffset],
                   xList[i]);
                int val = yList[i];
                int highRoom = range - predicted;
                int lowRoom = predicted;
                int room = highRoom < lowRoom ? highRoom * 2 : lowRoom * 2;
                if (val != 0)
                {
                    step2Flags[lowNeighbourOffset] = true;
                    step2Flags[highNeighbourOffset] = true;
                    step2Flags[i] = true;
                    if (val >= room)
                    {
                        yList[i] = highRoom > lowRoom ?
                           val - lowRoom + predicted :
                           -val + highRoom + predicted - 1;
                    }
                    else
                    {
                        yList[i] = (val & 1) == 1 ?
                           predicted - ((val + 1) >> 1) :
                           predicted + (val >> 1);
                    }

                }
                else
                {
                    step2Flags[i] = false;
                    yList[i] = predicted;
                }
            }

            int[] xList2 = new int[values];

            Array.Copy(xList, xList2, values);

            Sort(xList2, yList, step2Flags);

            int hx = 0, hy = 0, lx = 0, ly = yList[0] * multiplier;

            for (int i = 1; i < values; i++)
            {
                if (step2Flags[i])
                {
                    hy = yList[i] * multiplier;
                    hx = xList2[i];
                    Util.RenderLine(lx, ly, hx, hy, vector);
                    lx = hx;
                    ly = hy;
                }
            }

            float r = DB_STATIC_TABLE[hy];
            for (; hx < n / 2; vector[hx++] = r) ;
        }

        public Object Clone()
        {
            Floor1 clone = new Floor1();
            clone.classDimensions = classDimensions;
            clone.classMasterbooks = classMasterbooks;
            clone.classSubclasses = classSubclasses;
            clone.maximumClass = maximumClass;
            clone.multiplier = multiplier;
            clone.partitionClassList = partitionClassList;
            clone.rangeBits = rangeBits;
            clone.subclassBooks = subclassBooks;
            clone.xList = xList;
            clone.yList = yList;
            clone.lowNeighbours = lowNeighbours;
            clone.highNeighbours = highNeighbours;
            return clone;
        }

        private static void Sort(int[] x, int[] y, bool[] b)
        {
            int off = 0;
            int len = x.Length;
            for (int i = off; i < len + off; i++)
            {
                for (int j = i; j > off && x[j - 1] > x[j]; j--)
                {
                    Swap(x, j, j - 1);
                    Swap(y, j, j - 1);
                    Swap(b, j, j - 1);
                }
            }
        }

        private static void Swap(int[] x, int a, int b)
        {
            int t = x[a];
            x[a] = x[b];
            x[b] = t;
        }

        private static void Swap(bool[] x, int a, int b)
        {
            bool t = x[a];
            x[a] = x[b];
            x[b] = t;
        }
    }
}